Implement StrategyProvider - Jules#56
Conversation
- Introduced Strategy marker interface and StrategyProvider interface. - Refactored SomeConfig to implement StrategyProvider using an internal map. - Updated SomeConfigBuilder to support strategy() registration using an inline reified function. - Renamed SomeConfigBuilder.register() to factory() and provided a deprecated alias for backward compatibility. - Updated FixtureContext and ResolverChain to consume StrategyProvider instead of individual strategies. - Updated all strategy-consuming resolvers (Nullable, String, List, Set, Map, Array) and ClassResolver to use the new provider pattern. - Updated all unit and integration tests to reflect API changes. - Updated documentation and KDocs to match the new implementation. Co-authored-by: MessiasLima <10220064+MessiasLima@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Code Review
This pull request refactors the configuration system to use a dynamic strategy registry instead of hardcoded strategy fields. It introduces a Strategy marker interface and a StrategyProvider interface, updating SomeConfig, SomeConfigBuilder, and all resolvers to fetch strategies dynamically. Additionally, register is deprecated in favor of factory in SomeConfigBuilder. The review feedback highlights two important points: first, a critical bug in SomeConfigBuilder.strategy where registering a strategy with a specific subclass registers it under the subclass key instead of the base strategy interface key, causing resolvers to ignore it; second, an improvement in SomeConfig.get to separate the null check from the type cast to ensure clearer error messages in case of type mismatches.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| inline fun <reified T : Strategy> strategy(strategy: T) { | ||
| _strategies[T::class] = strategy | ||
| } |
There was a problem hiding this comment.
When registering a strategy using strategy(MyStrategy()) or strategy(NullableStrategy.AlwaysNull), the reified type T is inferred as the specific subclass/implementation type (e.g., NullableStrategy.AlwaysNull). This registers the strategy in _strategies under the key NullableStrategy.AlwaysNull::class.
However, the built-in resolvers (like NullableResolver, StringResolver, etc.) query the StrategyProvider using the base strategy interface class (e.g., NullableStrategy::class). Since the base interface key in the map is not updated, the custom strategy is completely ignored by the resolvers, and they continue to use the default strategy.
To fix this, we should also update any keys in _strategies that are supertypes of the registered strategy (i.e., where key.isInstance(strategy) is true).
inline fun <reified T : Strategy> strategy(strategy: T) {
_strategies[T::class] = strategy
_strategies.keys.toList().forEach { key ->
if (key.isInstance(strategy)) {
_strategies[key] = strategy
}
}
}| @Suppress("UNCHECKED_CAST") | ||
| override fun <T : Strategy> get(key: KClass<T>): T { | ||
| return strategies[key] as? T | ||
| ?: throw IllegalArgumentException("Strategy ${key.simpleName} not registered") | ||
| } |
There was a problem hiding this comment.
Using as? T and throwing a "not registered" exception can be misleading if the strategy is registered but the cast fails due to a type mismatch. Performing an explicit null check first allows us to throw the correct IllegalArgumentException for missing strategies, while letting the cast throw a ClassCastException if there is an unexpected type mismatch.
| @Suppress("UNCHECKED_CAST") | |
| override fun <T : Strategy> get(key: KClass<T>): T { | |
| return strategies[key] as? T | |
| ?: throw IllegalArgumentException("Strategy ${key.simpleName} not registered") | |
| } | |
| @Suppress("UNCHECKED_CAST") | |
| override fun <T : Strategy> get(key: KClass<T>): T { | |
| val strategy = strategies[key] ?: throw IllegalArgumentException("Strategy ${key.simpleName} not registered") | |
| return strategy as T | |
| } |
- Introduced Strategy marker interface and StrategyProvider interface. - Refactored SomeConfig to implement StrategyProvider using an internal map. - Updated SomeConfigBuilder to support strategy() registration using an inline reified function. - Renamed SomeConfigBuilder.register() to factory() and provided a deprecated alias for backward compatibility. - Updated FixtureContext and ResolverChain to consume StrategyProvider instead of individual strategies. - Updated all strategy-consuming resolvers (Nullable, String, List, Set, Map, Array) and ClassResolver to use the new provider pattern. - Updated all unit and integration tests to reflect API changes. - Updated documentation and KDocs to match the new implementation. - Fixed Detekt naming convention issue in SomeConfigBuilder. Co-authored-by: MessiasLima <10220064+MessiasLima@users.noreply.github.com>
…73005225355871000
…entation-13573005225355871000' into feature/strategy-provider-implementation-13573005225355871000
This change decouples strategy definitions from SomeConfig and the resolver chain by introducing a StrategyProvider. Users can now register custom strategies using
strategy(MyStrategy())and resolvers retrieve them viastrategyProvider[MyStrategy::class]. This removes the maintenance bottleneck where every new strategy required manual wiring in multiple files. Additionally,register()was renamed tofactory()to better differentiate it from strategy registration.Fixes #35
PR created automatically by Jules for task 13573005225355871000 started by @MessiasLima